package com.wpw.web.util;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.*;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.*;
import org.apache.http.conn.HttpConnectionFactory;
import org.apache.http.conn.ManagedHttpClientConnection;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.DefaultHttpResponseParser;
import org.apache.http.impl.conn.DefaultHttpResponseParserFactory;
import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.io.DefaultHttpRequestWriterFactory;
import org.apache.http.io.HttpMessageParser;
import org.apache.http.io.HttpMessageParserFactory;
import org.apache.http.io.HttpMessageWriterFactory;
import org.apache.http.io.SessionInputBuffer;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicLineParser;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.message.LineParser;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.CharArrayBuffer;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.CodingErrorAction;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * author:dengxiaming@edaijia-staff.cn crate_time:2014年5月1日 -下午4:02:06
 * description:http通信工具
 */

public class HttpUtil {
	/** milliseconds */
	private static final int TIME_OUT = 30 * 1000;
	private static final String UTF8 = "UTF-8";

	/**
	 * @param params : key : value
	 * @return
	 */
	public static String get(String url, Map<String, String> params) {
		String result = "";
		String getUrl = url;

		HttpMessageParserFactory<HttpResponse> responseParserFactory = new DefaultHttpResponseParserFactory() {

			@Override
			public HttpMessageParser<HttpResponse> create(
					SessionInputBuffer buffer, MessageConstraints constraints) {
				LineParser lineParser = new BasicLineParser() {

					@Override
					public Header parseHeader(final CharArrayBuffer buffer) {
						try {
							return super.parseHeader(buffer);
						} catch (ParseException ex) {
							return new BasicHeader(buffer.toString(), null);
						}
					}

				};
				return new DefaultHttpResponseParser(
						buffer, lineParser, DefaultHttpResponseFactory.INSTANCE, constraints) {

					@Override
					protected boolean reject(final CharArrayBuffer line, int count) {
						// try to ignore all garbage preceding a status line infinitely
						return false;
					}

				};
			}

		};
		HttpMessageWriterFactory<HttpRequest> requestWriterFactory = new DefaultHttpRequestWriterFactory();

		// Use a custom connection factory to customize the process of
		// initialization of outgoing HTTP connections. Beside standard connection
		// configuration parameters HTTP connection factory can define message
		// parser / writer routines to be employed by individual connections.
		HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory = new ManagedHttpClientConnectionFactory(
				requestWriterFactory, responseParserFactory);

		// Client HTTP connection objects when fully initialized can be bound to
		// an arbitrary network socket. The process of network socket initialization,
		// its connection to a remote address and binding to a local one is controlled
		// by a connection socket factory.

		// SSL context for secure connections can be created either based on
		// system or application specific properties.
		SSLContext sslcontext = SSLContexts.createSystemDefault();

		// Create a registry of custom connection socket factories for supported
		// protocol schemes.
		Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
				.register("http", PlainConnectionSocketFactory.INSTANCE)
				.register("https", new SSLConnectionSocketFactory(sslcontext))
				.build();

		// Use custom DNS resolver to override the system DNS resolution.
        /*DnsResolver dnsResolver = new SystemDefaultDnsResolver() {

            @Override
            public InetAddress[] resolve(final String host) throws UnknownHostException {
                if (host.equalsIgnoreCase("myhost")) {
                    return new InetAddress[] { InetAddress.getByAddress(new byte[] {127, 0, 0, 1}) };
                } else {
                    return super.resolve(host);
                }
            }

        };*/

		// Create a connection manager with custom configuration.
		PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(
				socketFactoryRegistry, connFactory);

		// Create socket configuration
		SocketConfig socketConfig = SocketConfig.custom()
				.setTcpNoDelay(true)
				.build();
		// Configure the connection manager to use socket configuration either
		// by default or for a specific host.
		connManager.setDefaultSocketConfig(socketConfig);
		// connManager.setSocketConfig(new HttpHost("somehost", 80), socketConfig);
		// Validate connections after 1 sec of inactivity
		connManager.setValidateAfterInactivity(1000);

		// Create message constraints
       /* MessageConstraints messageConstraints = MessageConstraints.custom()
                .setMaxHeaderCount(200)
                .setMaxLineLength(2000)
                .build();*/
		// Create connection configuration
		ConnectionConfig connectionConfig = ConnectionConfig.custom()
				.setMalformedInputAction(CodingErrorAction.IGNORE)
				.setUnmappableInputAction(CodingErrorAction.IGNORE)
				.setCharset(Consts.UTF_8)
				// .setMessageConstraints(messageConstraints)
				.build();
		// Configure the connection manager to use connection configuration either
		// by default or for a specific host.
		connManager.setDefaultConnectionConfig(connectionConfig);
		// connManager.setConnectionConfig(new HttpHost("somehost", 80), ConnectionConfig.DEFAULT);

		// Configure total max or per route limits for persistent connections
		// that can be kept in the pool or leased by the connection manager.
		connManager.setMaxTotal(100);
		connManager.setDefaultMaxPerRoute(20);
		// connManager.setMaxPerRoute(new HttpRoute(new HttpHost("somehost", 80)), 20);

		// Use custom cookie store if necessary.
		CookieStore cookieStore = new BasicCookieStore();
		// Use custom credentials provider if necessary.
		CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
		// 设置请求和传输超时时间
		RequestConfig requestConfig = RequestConfig.custom()
				.setSocketTimeout(TIME_OUT)
				.setConnectTimeout(TIME_OUT)
				.setConnectionRequestTimeout(TIME_OUT)
				.build();

		// Create an HttpClient with the given custom dependencies and configuration.
		CloseableHttpClient httpClient = HttpClients.custom()
				.setConnectionManager(connManager)
				.setDefaultCookieStore(cookieStore)
				.setDefaultCredentialsProvider(credentialsProvider)
				// .setProxy(new HttpHost("myproxy", 8080))
				.setDefaultRequestConfig(requestConfig)
				.evictExpiredConnections()
				// .evictIdleConnections(10, TimeUnit.SECONDS)
				.build();

		if (params != null) {
			getUrl = makeGetURL(url, params);
		}

		HttpGet httpGet = new HttpGet(getUrl);
		try {
			// Execution context can be customized locally.
			HttpClientContext context = HttpClientContext.create();
			// Contextual attributes set the local context level will take
			// precedence over those set at the client level.
			context.setCookieStore(cookieStore);
			context.setCredentialsProvider(credentialsProvider);

			httpGet.setConfig(requestConfig);

			// 执行get请求
			CloseableHttpResponse httpResponse = httpClient.execute(httpGet, context);

			try{
				// 响应状态
				int code = httpResponse.getStatusLine().getStatusCode();
				if (code == 200) {
					// 获取响应消息实体
					HttpEntity entity = httpResponse.getEntity();

					// 判断响应实体是否为空
					if (entity != null) {
						result = EntityUtils.toString(entity);
						// 公用类中不再打印返回日志，谁用谁自己打印--李卫/王佳辉 2019-10-21
//						logger.info(String.format("http get response:%s", result));
					}
				}
			} finally {
				if(httpResponse != null){
					httpResponse.close();
				}
			}
		} catch (IOException e) {
			result = null;
		} catch (Exception e){
		} finally {
			try {
				// 关闭流并释放资源
				httpGet.releaseConnection();
				httpClient.close();
			}
			catch (IOException e) {
			}
			catch (Exception e) {
			}
		}
		return result;
	}

	public static String post(String url, Map<String, ?> params) {
		String result = "";
		HttpMessageParserFactory<HttpResponse> responseParserFactory = new DefaultHttpResponseParserFactory() {

			@Override
			public HttpMessageParser<HttpResponse> create(
					SessionInputBuffer buffer, MessageConstraints constraints) {
				LineParser lineParser = new BasicLineParser() {

					@Override
					public Header parseHeader(final CharArrayBuffer buffer) {
						try {
							return super.parseHeader(buffer);
						} catch (ParseException ex) {
							return new BasicHeader(buffer.toString(), null);
						}
					}

				};
				return new DefaultHttpResponseParser(
						buffer, lineParser, DefaultHttpResponseFactory.INSTANCE, constraints) {

					@Override
					protected boolean reject(final CharArrayBuffer line, int count) {
						// try to ignore all garbage preceding a status line infinitely
						return false;
					}

				};
			}

		};
		HttpMessageWriterFactory<HttpRequest> requestWriterFactory = new DefaultHttpRequestWriterFactory();

		// Use a custom connection factory to customize the process of
		// initialization of outgoing HTTP connections. Beside standard connection
		// configuration parameters HTTP connection factory can define message
		// parser / writer routines to be employed by individual connections.
		HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory = new ManagedHttpClientConnectionFactory(
				requestWriterFactory, responseParserFactory);

		// Client HTTP connection objects when fully initialized can be bound to
		// an arbitrary network socket. The process of network socket initialization,
		// its connection to a remote address and binding to a local one is controlled
		// by a connection socket factory.

		// SSL context for secure connections can be created either based on
		// system or application specific properties.
		SSLContext sslcontext = SSLContexts.createSystemDefault();

		// Create a registry of custom connection socket factories for supported
		// protocol schemes.
		Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
				.register("http", PlainConnectionSocketFactory.INSTANCE)
				.register("https", new SSLConnectionSocketFactory(sslcontext))
				.build();

		// Use custom DNS resolver to override the system DNS resolution.
        /*DnsResolver dnsResolver = new SystemDefaultDnsResolver() {

            @Override
            public InetAddress[] resolve(final String host) throws UnknownHostException {
                if (host.equalsIgnoreCase("myhost")) {
                    return new InetAddress[] { InetAddress.getByAddress(new byte[] {127, 0, 0, 1}) };
                } else {
                    return super.resolve(host);
                }
            }

        };*/

		// Create a connection manager with custom configuration.
		PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(
				socketFactoryRegistry, connFactory);

		// Create socket configuration
		SocketConfig socketConfig = SocketConfig.custom()
				.setTcpNoDelay(true)
				.build();
		// Configure the connection manager to use socket configuration either
		// by default or for a specific host.
		connManager.setDefaultSocketConfig(socketConfig);
		// connManager.setSocketConfig(new HttpHost("somehost", 80), socketConfig);
		// Validate connections after 1 sec of inactivity
		connManager.setValidateAfterInactivity(1000);

		// Create message constraints
       /* MessageConstraints messageConstraints = MessageConstraints.custom()
                .setMaxHeaderCount(200)
                .setMaxLineLength(2000)
                .build();*/
		// Create connection configuration
		ConnectionConfig connectionConfig = ConnectionConfig.custom()
				.setMalformedInputAction(CodingErrorAction.IGNORE)
				.setUnmappableInputAction(CodingErrorAction.IGNORE)
				.setCharset(Consts.UTF_8)
				// .setMessageConstraints(messageConstraints)
				.build();
		// Configure the connection manager to use connection configuration either
		// by default or for a specific host.
		connManager.setDefaultConnectionConfig(connectionConfig);
		// connManager.setConnectionConfig(new HttpHost("somehost", 80), ConnectionConfig.DEFAULT);

		// Configure total max or per route limits for persistent connections
		// that can be kept in the pool or leased by the connection manager.
		connManager.setMaxTotal(100);
		connManager.setDefaultMaxPerRoute(20);
		// connManager.setMaxPerRoute(new HttpRoute(new HttpHost("somehost", 80)), 20);

		// Use custom cookie store if necessary.
		CookieStore cookieStore = new BasicCookieStore();
		// Use custom credentials provider if necessary.
		CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
		// Create global request configuration
		// 设置请求和传输超时时间
		RequestConfig requestConfig = RequestConfig.custom()
				.setSocketTimeout(TIME_OUT)
				.setConnectTimeout(TIME_OUT)
				.setConnectionRequestTimeout(TIME_OUT)
				.build();

		// Create an HttpClient with the given custom dependencies and configuration.
		CloseableHttpClient httpClient = HttpClients.custom()
				.setConnectionManager(connManager)
				.setDefaultCookieStore(cookieStore)
				.setDefaultCredentialsProvider(credentialsProvider)
				// .setProxy(new HttpHost("myproxy", 8080))
				.setDefaultRequestConfig(requestConfig)
				.evictExpiredConnections()
				.evictIdleConnections(10L, TimeUnit.SECONDS)
				.build();
		HttpPost httpPost = new HttpPost(url);
		try {
			httpPost.setConfig(requestConfig);

			// Execution context can be customized locally.
			HttpClientContext context = HttpClientContext.create();
			// Contextual attributes set the local context level will take
			// precedence over those set at the client level.
			context.setCookieStore(cookieStore);
			context.setCredentialsProvider(credentialsProvider);

			// 创建参数队列
			List<NameValuePair> formParams = parseRequestPost(params);
			httpPost.setEntity(new UrlEncodedFormEntity(formParams, UTF8));
			CloseableHttpResponse response = httpClient.execute(httpPost, context);
			try{
				int code = response.getStatusLine().getStatusCode();
				if (code == 200) {
					HttpEntity entity = response.getEntity();
					result = EntityUtils.toString(entity, UTF8);
					// 公用类中不再打印返回日志，谁用谁自己打印--李卫/王佳辉 2019-10-21
//					logger.info(String.format("post result:%s", result));
				}
				else {
				}
			} catch (Exception e){
			}finally {
				if(response != null){
					response.close();
				}
			}
		}
		catch (UnsupportedEncodingException e) {
		}
		catch (IOException e) {
		} catch (Exception e){
		} finally {
			try {
				httpPost.releaseConnection();
				httpClient.close();
			}
			catch (IOException e) {
			}
			catch (Exception e) {
			}
		}

		return result;
	}

	public static String post(String url, StringEntity body) {
		return post(url, body, null);
	}

	public static String post(String url, StringEntity body, Map<String, String> headMap) {
		String result = "";

		// Client HTTP connection objects when fully initialized can be bound to
		// an arbitrary network socket. The process of network socket initialization,
		// its connection to a remote address and binding to a local one is controlled
		// by a connection socket factory.

		// SSL context for secure connections can be created either based on
		// system or application specific properties.
		SSLContext sslcontext = SSLContexts.createSystemDefault();

		// Create a registry of custom connection socket factories for supported
		// protocol schemes.
		Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
				.register("http", PlainConnectionSocketFactory.INSTANCE)
				.register("https", new SSLConnectionSocketFactory(sslcontext))
				.build();

		// Create a connection manager with custom configuration.
		PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(
				socketFactoryRegistry);

		// Create socket configuration
		SocketConfig socketConfig = SocketConfig.custom()
				.setTcpNoDelay(true)
				.build();
		// Configure the connection manager to use socket configuration either
		// by default or for a specific host.
		connManager.setDefaultSocketConfig(socketConfig);
		// connManager.setSocketConfig(new HttpHost("somehost", 80), socketConfig);
		// Validate connections after 1 sec of inactivity
		connManager.setValidateAfterInactivity(1000);
		// Create connection configuration
		ConnectionConfig connectionConfig = ConnectionConfig.custom()
				.setMalformedInputAction(CodingErrorAction.IGNORE)
				.setUnmappableInputAction(CodingErrorAction.IGNORE)
				.setCharset(Consts.UTF_8)
				// .setMessageConstraints(messageConstraints)
				.build();
		// Configure the connection manager to use connection configuration either
		// by default or for a specific host.
		connManager.setDefaultConnectionConfig(connectionConfig);
		// connManager.setConnectionConfig(new HttpHost("somehost", 80), ConnectionConfig.DEFAULT);

		// Configure total max or per route limits for persistent connections
		// that can be kept in the pool or leased by the connection manager.
		connManager.setMaxTotal(100);
		connManager.setDefaultMaxPerRoute(20);
		// connManager.setMaxPerRoute(new HttpRoute(new HttpHost("somehost", 80)), 20);

		// Use custom cookie store if necessary.
		CookieStore cookieStore = new BasicCookieStore();
		// Use custom credentials provider if necessary.
		CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
		RequestConfig requestConfig = RequestConfig.custom()
				.setSocketTimeout(TIME_OUT)
				.setConnectTimeout(TIME_OUT)
				.setConnectionRequestTimeout(TIME_OUT)
				.build();// 设置请求和传输超时时间

		// Create an HttpClient with the given custom dependencies and configuration.
		CloseableHttpClient httpClient = HttpClients.custom()
				.setConnectionManager(connManager)
				.setDefaultCookieStore(cookieStore)
				.setDefaultCredentialsProvider(credentialsProvider)
				.setDefaultRequestConfig(requestConfig)
				.evictExpiredConnections()
				.evictIdleConnections(10L, TimeUnit.SECONDS)
				.build();
		HttpPost httpPost = new HttpPost(url);
		try {
			httpPost.setConfig(requestConfig);
			// Execution context can be customized locally.
			HttpClientContext context = HttpClientContext.create();
			// Contextual attributes set the local context level will take
			// precedence over those set at the client level.
			context.setCookieStore(cookieStore);
			context.setCredentialsProvider(credentialsProvider);
			httpPost.setEntity(body);

			if(null != headMap && headMap.size() > 0){
				for(Map.Entry<String, String> entry : headMap.entrySet()){
					httpPost.addHeader(entry.getKey(), entry.getValue());
				}
			}

			// 创建参数队列
			CloseableHttpResponse response = httpClient.execute(httpPost, context);
			try{
				int code = response.getStatusLine().getStatusCode();
				if (code == 200) {
					HttpEntity entity = response.getEntity();
					result = EntityUtils.toString(entity, UTF8);
					// 公用类中不再打印返回日志，谁用谁自己打印--李卫/王佳辉 2019-10-21
//					logger.info(String.format("post StringEntity result :%s", result));
				}
				else {
				}
			} finally {
				if(response != null){
					response.close();
				}
			}
		}
		catch (UnsupportedEncodingException e) {
		}
		catch (IOException e) {
		}catch (Exception e){
		}
		finally {
			try {
				httpPost.releaseConnection();
				httpClient.close();
			}
			catch (IOException e) {
			}
			catch (Exception e) {
			}
		}

		return result;
	}

	public static String post(String url, Map<String, ?> params,int timeOut) {
		String result = "";
		HttpMessageParserFactory<HttpResponse> responseParserFactory = new DefaultHttpResponseParserFactory() {

			@Override
			public HttpMessageParser<HttpResponse> create(
					SessionInputBuffer buffer, MessageConstraints constraints) {
				LineParser lineParser = new BasicLineParser() {

					@Override
					public Header parseHeader(final CharArrayBuffer buffer) {
						try {
							return super.parseHeader(buffer);
						} catch (ParseException ex) {
							return new BasicHeader(buffer.toString(), null);
						}
					}

				};
				return new DefaultHttpResponseParser(
						buffer, lineParser, DefaultHttpResponseFactory.INSTANCE, constraints) {

					@Override
					protected boolean reject(final CharArrayBuffer line, int count) {
						// try to ignore all garbage preceding a status line infinitely
						return false;
					}

				};
			}

		};
		HttpMessageWriterFactory<HttpRequest> requestWriterFactory = new DefaultHttpRequestWriterFactory();

		// Use a custom connection factory to customize the process of
		// initialization of outgoing HTTP connections. Beside standard connection
		// configuration parameters HTTP connection factory can define message
		// parser / writer routines to be employed by individual connections.
		HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory = new ManagedHttpClientConnectionFactory(
				requestWriterFactory, responseParserFactory);

		// Client HTTP connection objects when fully initialized can be bound to
		// an arbitrary network socket. The process of network socket initialization,
		// its connection to a remote address and binding to a local one is controlled
		// by a connection socket factory.

		// SSL context for secure connections can be created either based on
		// system or application specific properties.
		SSLContext sslcontext = SSLContexts.createSystemDefault();

		// Create a registry of custom connection socket factories for supported
		// protocol schemes.
		Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
				.register("http", PlainConnectionSocketFactory.INSTANCE)
				.register("https", new SSLConnectionSocketFactory(sslcontext))
				.build();

		// Use custom DNS resolver to override the system DNS resolution.
        /*DnsResolver dnsResolver = new SystemDefaultDnsResolver() {

            @Override
            public InetAddress[] resolve(final String host) throws UnknownHostException {
                if (host.equalsIgnoreCase("myhost")) {
                    return new InetAddress[] { InetAddress.getByAddress(new byte[] {127, 0, 0, 1}) };
                } else {
                    return super.resolve(host);
                }
            }

        };*/

		// Create a connection manager with custom configuration.
		PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(
				socketFactoryRegistry, connFactory);

		// Create socket configuration
		SocketConfig socketConfig = SocketConfig.custom()
				.setTcpNoDelay(true)
				.build();
		// Configure the connection manager to use socket configuration either
		// by default or for a specific host.
		connManager.setDefaultSocketConfig(socketConfig);
		// connManager.setSocketConfig(new HttpHost("somehost", 80), socketConfig);
		// Validate connections after 1 sec of inactivity
		connManager.setValidateAfterInactivity(1000);

		// Create message constraints
       /* MessageConstraints messageConstraints = MessageConstraints.custom()
                .setMaxHeaderCount(200)
                .setMaxLineLength(2000)
                .build();*/
		// Create connection configuration
		ConnectionConfig connectionConfig = ConnectionConfig.custom()
				.setMalformedInputAction(CodingErrorAction.IGNORE)
				.setUnmappableInputAction(CodingErrorAction.IGNORE)
				.setCharset(Consts.UTF_8)
				// .setMessageConstraints(messageConstraints)
				.build();
		// Configure the connection manager to use connection configuration either
		// by default or for a specific host.
		connManager.setDefaultConnectionConfig(connectionConfig);
		// connManager.setConnectionConfig(new HttpHost("somehost", 80), ConnectionConfig.DEFAULT);

		// Configure total max or per route limits for persistent connections
		// that can be kept in the pool or leased by the connection manager.
		connManager.setMaxTotal(100);
		connManager.setDefaultMaxPerRoute(20);
		// connManager.setMaxPerRoute(new HttpRoute(new HttpHost("somehost", 80)), 20);

		// Use custom cookie store if necessary.
		CookieStore cookieStore = new BasicCookieStore();
		// Use custom credentials provider if necessary.
		CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
		// Create global request configuration
		// 设置请求和传输超时时间
		RequestConfig requestConfig = RequestConfig.custom()
				.setSocketTimeout(timeOut)
				.setConnectTimeout(timeOut)
				.setConnectionRequestTimeout(timeOut)
				.build();

		// Create an HttpClient with the given custom dependencies and configuration.
		CloseableHttpClient httpClient = HttpClients.custom()
				.setConnectionManager(connManager)
				.setDefaultCookieStore(cookieStore)
				.setDefaultCredentialsProvider(credentialsProvider)
				// .setProxy(new HttpHost("myproxy", 8080))
				.setDefaultRequestConfig(requestConfig)
				.evictExpiredConnections()
				.evictIdleConnections(10L, TimeUnit.SECONDS)
				.build();
		HttpPost httpPost = new HttpPost(url);
		try {
			httpPost.setConfig(requestConfig);

			// Execution context can be customized locally.
			HttpClientContext context = HttpClientContext.create();
			// Contextual attributes set the local context level will take
			// precedence over those set at the client level.
			context.setCookieStore(cookieStore);
			context.setCredentialsProvider(credentialsProvider);

			// 创建参数队列
			List<NameValuePair> formParams = parseRequestPost(params);
			httpPost.setEntity(new UrlEncodedFormEntity(formParams, UTF8));
			CloseableHttpResponse response = httpClient.execute(httpPost, context);
			try{
				int code = response.getStatusLine().getStatusCode();
				if (code == 200) {
					HttpEntity entity = response.getEntity();
					result = EntityUtils.toString(entity, UTF8);
					// 公用类中不再打印返回日志，谁用谁自己打印--李卫/王佳辉 2019-10-21
//					logger.info(String.format("post result:%s", result));
				}
				else {
				}
			} catch (Exception e){
			}finally {
				if(response != null){
					response.close();
				}
			}
		}
		catch (UnsupportedEncodingException e) {
		}
		catch (IOException e) {
		} catch (Exception e){
		} finally {
			try {
				httpPost.releaseConnection();
				httpClient.close();
			}
			catch (IOException e) {
			}
			catch (Exception e) {
			}
		}

		return result;
	}

	public static String post(String url, StringEntity body,int timeOut) {
		String result = "";

		// Client HTTP connection objects when fully initialized can be bound to
		// an arbitrary network socket. The process of network socket initialization,
		// its connection to a remote address and binding to a local one is controlled
		// by a connection socket factory.

		// SSL context for secure connections can be created either based on
		// system or application specific properties.
		SSLContext sslcontext = SSLContexts.createSystemDefault();

		// Create a registry of custom connection socket factories for supported
		// protocol schemes.
		Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
				.register("http", PlainConnectionSocketFactory.INSTANCE)
				.register("https", new SSLConnectionSocketFactory(sslcontext))
				.build();

		// Create a connection manager with custom configuration.
		PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(
				socketFactoryRegistry);

		// Create socket configuration
		SocketConfig socketConfig = SocketConfig.custom()
				.setTcpNoDelay(true)
				.build();
		// Configure the connection manager to use socket configuration either
		// by default or for a specific host.
		connManager.setDefaultSocketConfig(socketConfig);
		// connManager.setSocketConfig(new HttpHost("somehost", 80), socketConfig);
		// Validate connections after 1 sec of inactivity
		connManager.setValidateAfterInactivity(1000);
		// Create connection configuration
		ConnectionConfig connectionConfig = ConnectionConfig.custom()
				.setMalformedInputAction(CodingErrorAction.IGNORE)
				.setUnmappableInputAction(CodingErrorAction.IGNORE)
				.setCharset(Consts.UTF_8)
				// .setMessageConstraints(messageConstraints)
				.build();
		// Configure the connection manager to use connection configuration either
		// by default or for a specific host.
		connManager.setDefaultConnectionConfig(connectionConfig);
		// connManager.setConnectionConfig(new HttpHost("somehost", 80), ConnectionConfig.DEFAULT);

		// Configure total max or per route limits for persistent connections
		// that can be kept in the pool or leased by the connection manager.
		connManager.setMaxTotal(100);
		connManager.setDefaultMaxPerRoute(20);
		// connManager.setMaxPerRoute(new HttpRoute(new HttpHost("somehost", 80)), 20);

		// Use custom cookie store if necessary.
		CookieStore cookieStore = new BasicCookieStore();
		// Use custom credentials provider if necessary.
		CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
		RequestConfig requestConfig = RequestConfig.custom()
				.setSocketTimeout(timeOut)
				.setConnectTimeout(timeOut)
				.setConnectionRequestTimeout(timeOut)
				.build();// 设置请求和传输超时时间

		// Create an HttpClient with the given custom dependencies and configuration.
		CloseableHttpClient httpClient = HttpClients.custom()
				.setConnectionManager(connManager)
				.setDefaultCookieStore(cookieStore)
				.setDefaultCredentialsProvider(credentialsProvider)
				.setDefaultRequestConfig(requestConfig)
				.evictExpiredConnections()
				.evictIdleConnections(10L, TimeUnit.SECONDS)
				.build();
		HttpPost httpPost = new HttpPost(url);
		try {
			httpPost.setConfig(requestConfig);
			// Execution context can be customized locally.
			HttpClientContext context = HttpClientContext.create();
			// Contextual attributes set the local context level will take
			// precedence over those set at the client level.
			context.setCookieStore(cookieStore);
			context.setCredentialsProvider(credentialsProvider);
			httpPost.setEntity(body);
			// 创建参数队列
			CloseableHttpResponse response = httpClient.execute(httpPost, context);
			try{
				int code = response.getStatusLine().getStatusCode();
				if (code == 200) {
					HttpEntity entity = response.getEntity();
					result = EntityUtils.toString(entity, UTF8);
					// 公用类中不再打印返回日志，谁用谁自己打印--李卫/王佳辉 2019-10-21
//					logger.info(String.format("post StringEntity result :%s", result));
				}
				else {
				}
			} finally {
				if(response != null){
					response.close();
				}
			}
		}
		catch (UnsupportedEncodingException e) {
		}
		catch (IOException e) {
		}catch (Exception e){
		}
		finally {
			try {
				httpPost.releaseConnection();
				httpClient.close();
			}
			catch (IOException e) {
			}
			catch (Exception e) {
			}
		}

		return result;
	}

	/**
	 * 解析返回结果
	 *
	 * @param method
	 * @return
	 */
	private static List<NameValuePair> parseRequestPost(Map<String, ?> params) {
		List<NameValuePair> list = new ArrayList<NameValuePair>();
		try {
			Iterator<String> paramKeys = params.keySet().iterator();
			String key = null;
			while (paramKeys.hasNext()) {
				key = paramKeys.next();
				Object value = params.get(key);
				if (value != null) {
					list.add(new BasicNameValuePair(key, value.toString()));
				}
			}
		}
		catch (Exception e) {
		}
		return list;
	}

	/**
	 * 拼接get 请求 url
	 * 
	 * @param baseURL
	 * @param params
	 * @return
	 */
	private static String makeGetURL(String baseURL, Map<String, String> params) {

		StringBuilder sb = new StringBuilder();
		Iterator<String> paramKeys = params.keySet().iterator();
		sb.append(baseURL).append("?");
		while (paramKeys.hasNext()) {
			String key = paramKeys.next();
			String value = params.get(key);
			sb.append(key);
			sb.append("=");
			sb.append(value);
			sb.append("&");
		}
		return sb.substring(0, sb.length() - 1);
	}

	/**
	 * 用于对于汉字转码
	 *
	 * @param input
	 * @return
	 */
	public static String encode(String input) {
		if (StringUtils.isNotBlank(input)) {
			try {
				return URLEncoder.encode(input, "UTF-8");
			}
			catch (UnsupportedEncodingException e) {
			}
		}
		return input;
	}

	/**
	 * 生成多实例接口签名字符串
	 *
	 * @param params HTTP接口请求参数
	 * @return
	 */
	public static String genSaaSOpenSignature(Map<String, Object> params, String appKey) {
		StringBuilder sb = new StringBuilder();
		TreeSet<String> keys = new TreeSet<>(params.keySet());
		for (String key : keys) {
			sb.append(key).append("=");
			sb.append(params.get(key)).append("&");
		}
		sb.append("appKey=").append(appKey);
		return DigestUtils.md5Hex(sb.toString());
	}

	/**
	 * 生成多实例接口签名字符串
	 *
	 * @param params HTTP接口请求参数
	 * @return
	 */
	public static String genSaaSOpenSignature(JSONObject jsonObject, String appKey) {
		StringBuilder sb = new StringBuilder();
		TreeSet<String> keys = new TreeSet<>(jsonObject.keySet());
		for (String key : keys) {
			sb.append(key).append("=");
			sb.append(jsonObject.get(key)).append("&");
		}
		sb.append("appKey=").append(appKey);
		return DigestUtils.md5Hex(sb.toString());
	}

	/**
	 * 生成CRM接口签名字符串
	 * 
	 * @param params HTTP接口请求参数
	 * @return
	 */
	public static String genCRMSignature(Map<String, String> params, String appKey) {
		StringBuilder sb = new StringBuilder();
		TreeSet<String> keys = new TreeSet<>(params.keySet());
		for (String key : keys) {
			sb.append(key);
			sb.append(params.get(key));
		}
		sb.append(appKey);
		return DigestUtils.md5Hex(sb.toString());
	}

	/**
	 * 生成接口签名字符串
	 * 
	 * @param params HTTP接口请求参数
	 * @return
	 */
	public static String genSignature(Map<String, String> params, String appKey) {
		StringBuilder sb = new StringBuilder();

		TreeSet<String> keys = new TreeSet<>(params.keySet());
		for (String key : keys) {
			sb.append(key).append("=");
			sb.append(params.get(key)).append("&");
		}

		sb.append("appKey=").append(appKey);
		return DigestUtils.md5Hex(sb.toString());
	}

}
